﻿#include <stdio.h>
#include <stdlib.h>

#include <iostream>

#include "facedetect.h"
#include "json/json.h"
#include "mongoose/mongoose.h"

const char *s_http_port = "12345";

typedef void (*CallBack)(struct mg_connection *nc, std::string url,
                         std::string data);

void ResponseJson(mg_connection *nc, const Json::Value v_result) {
    if (!nc) {
        return;
    }
    Json::FastWriter writer;
    std::string str;
    try {
        str = writer.write(v_result);
    } catch (std::exception &e) {
        str = e.what();
    }
    mg_printf(nc,
              "HTTP/1.1 200 OK\r\n"
              "Connection: close\r\n"
              "Content-Type: application/json\r\n"
              "Content-Length: %u\r\n\r\n"
              "%s\r\n",
              (uint32_t)str.length(), str.c_str());
    nc->flags |= MG_F_SEND_AND_CLOSE;
}

static void ev_handler(struct mg_connection *nc, int ev, void *ev_data) {
    struct http_message *hm = (struct http_message *)ev_data;
    CallBack cb = (CallBack)(nc->user_data);
    switch (ev) {
    case MG_EV_HTTP_REQUEST:
        if (mg_vcmp(&hm->method, "POST") == 0 ||
            mg_vcmp(&hm->method, "GET") == 0) {
            std::string url(hm->uri.p, hm->uri.len);
            std::string body(hm->body.p, hm->body.len);
            cb(nc, url, body);
        }
        break;
    case MG_EV_CLOSE:
        // std::cout << "---- Handle Closed ----" << std::endl;
        break;
    }
}

std::string data2base64(const char *data, int size) {
    char *pbase64 = (char *)calloc(size / 3 * 4 + 8, sizeof(char));
    mg_base64_encode((const unsigned char *)data, size, pbase64);
    std::string res(pbase64);
    free(pbase64);
    return res;
}

bool str2json(const std::string &str, Json::Value &v) {
    Json::Reader reader;
    Json::Value temp;
    if (!reader.parse(str, temp)) return false;
    v = temp;
    return true;
}

void ExFaceDetect(const char *img, long &len, Json::Value &v) {
    int count = FaceDetect((unsigned char *)(img), len);
    std::cout << "FaceCount: " << count << std::endl;
    if (count == 1) {
        std::string imagebase_new = data2base64((const char *)img, len);
        v["code"] = 0;
        v["image"] = imagebase_new;
        v["msg"] = "ok";
    } else {
        v["code"] = count;
        v["msg"] = "照片未检测到人脸或存在多张人脸";
    }
}

void JsonHandle(const std::string &data, Json::Value &v) {
    Json::Value temp;
    std::string imagebase;
    try {
        if (!str2json(data, temp)) throw "Json 格式错误";
        if (!temp.isMember("image")) throw "缺少 'image' 字段";
        imagebase = temp["image"].asString();
    } catch (const char *e) {
        std::cerr << e << '\n';
        v["code"] = -1;
        v["msg"] = e;
        return;
    } catch (const std::exception &e) {
        std::cerr << e.what() << '\n';
        v["code"] = -1;
        v["msg"] = e.what();
        return;
    }
    char *imageBinary = (char *)malloc(imagebase.size() * 2);
    long len = mg_base64_decode((const unsigned char *)(imagebase.c_str()),
                                imagebase.size(), imageBinary);
    ExFaceDetect((const char *)imageBinary, len, v);
    free(imageBinary);
    imageBinary = NULL;
}

void BinaryHandle(const std::string &str, Json::Value &v) {
    long len = str.size();
    char *imageBinary = (char *)malloc(len * 2);
    memcpy(imageBinary, str.c_str(), len);
    ExFaceDetect((const char *)imageBinary, len, v);
    free(imageBinary);
    imageBinary = NULL;
}

void HttpMessageHandle(struct mg_connection *nc, std::string url,
                   std::string data) {
    Json::Value v;
    if (url == "/facecut_base") {
        JsonHandle(data, v);
        ResponseJson(nc, v);
    } else if (url == "/facecut_binary") {
        BinaryHandle(data, v);
        ResponseJson(nc, v);
    }
}

int main(int argc, char **argv) {
    struct mg_mgr mgr;
    struct mg_connection *nc;

    mg_mgr_init(&mgr, NULL);
    nc = mg_bind(&mgr, s_http_port, ev_handler);
    mg_set_protocol_http_websocket(nc);
    nc->user_data = (void *)(HttpMessageHandle);
    for (;;) {
        mg_mgr_poll(&mgr, 200);
    }
    mg_mgr_free(&mgr);

    return 0;
}